using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Threading;

namespace CRC_DEMO
{
	public partial class Form1 : Form
	{
		private int CRC_LEN = 0;
		private byte[] CRC_POLYNOM = null;

		public Form1()
		{
			InitializeComponent();
			this.UpdateFormState(this, EventArgs.Empty);
		}

		private void UpdateFormState(object sender, EventArgs e)
		{
			groupBoxText.Enabled = radioButtonText.Checked;
			groupBoxFile.Enabled = radioButtonFile.Checked;

			if (radioButtonCRC_8.Checked)
			{
				CRC_LEN = 1;
				CRC_POLYNOM = new byte[] { 0xD5 };
			}
			else if (radioButtonCRC_16.Checked)
			{
				CRC_LEN = 2;
				CRC_POLYNOM = new byte[] { 0x80, 0x05 };
			}
			else if (radioButtonCRC_32.Checked)
			{
				CRC_LEN = 4;
				//CRC_POLYNOM = new byte[] { 0x04, 0xC1, 0x1D, 0xB7 };
				CRC_POLYNOM = new byte[] { 0xED, 0xB8, 0x83, 0x20 };
			}
			else if (radioButtonCRC_64.Checked)
			{
				CRC_LEN = 8;
				CRC_POLYNOM = new byte[] { 0x42, 0xF0, 0xE1, 0xEB, 0xA9, 0xEA, 0x36, 0x93 };
			}
		}

		private void buttonChoose_Click(object sender, EventArgs e)
		{
			if (openFileDialog.ShowDialog() == DialogResult.OK)
			{
				textBoxFileName.Text = openFileDialog.FileName;
			}
		}

		private void buttonCalc_Click(object sender, EventArgs e)
		{
			try
			{
				byte[] data = this.GetData();
				byte[] crc = this.CalcCRC(data);

				StringBuilder sb = GetBinaryString(crc);
				textBoxCRC_Result.Text = sb.ToString();
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}

		protected StringBuilder GetBinaryString(byte[] byte_arr)
		{
			StringBuilder sb = new StringBuilder();
			foreach (byte b in byte_arr)
			{
				byte mask = 128;
				while (mask > 0)
				{
					if ((b & mask) != 0)
						sb.Append('1');
					else
						sb.Append('0');
					mask >>= 1;
				}
			}
			return sb;
		}

		protected byte[] GetData()
		{
			if (radioButtonText.Checked)
			{
				return Encoding.ASCII.GetBytes(textBoxText.Text);
			}
			else
			{
				if (!File.Exists(textBoxFileName.Text))
				{
					throw new Exception("File \"" + textBoxFileName.Text + "\" not found!");
				}
				else
				{
					return File.ReadAllBytes(textBoxFileName.Text);
				}
			}
		}

		protected byte[] CalcCRC(byte[] data)
		{
			byte[] prep_data = new byte[data.Length + CRC_LEN + 1];
			prep_data[0] = 0xff;
			data.CopyTo(prep_data, 1);

			byte[] crc_res = new byte[CRC_LEN];
			int pos = 0;
			for (; pos < CRC_LEN; ++pos)
			{
				crc_res[pos] = prep_data[pos];
			}

			for (; pos < prep_data.Length; ++pos)
			{
				byte next = prep_data[pos];
				for (int i = 0; i < 8; ++i)
				{
					//Shift:
					bool was_hi_bit = (0x80 & crc_res[0]) == 0x80;
					for (int j = 0; j < CRC_LEN - 1; ++j)
					{
						crc_res[j] = (byte)(crc_res[j] << 1);
						if ((0x80 & crc_res[j + 1]) == 0x80)
							crc_res[j] |= 1;
					}
					crc_res[CRC_LEN - 1] = (byte)(crc_res[CRC_LEN - 1] << 1);
					if ((0x80 & next) == 0x80)
						crc_res[CRC_LEN - 1] |= 1;
					next = (byte)(next << 1);

					//Xor if neded:
					if (was_hi_bit)
					{
						for (int j = 0; j < CRC_LEN; ++j)
						{
							crc_res[j] ^= CRC_POLYNOM[j];
						}
					}
				}
			}
			return crc_res;
		}

		Random r = new Random(DateTime.Now.Second);
		private bool val_over;
		protected void MakeRandomErrors(byte[] data)
		{
			val_over = false;
			int err_cnt = r.Next(1, 5);
			for (; err_cnt > 0; --err_cnt)
			{
				int err_loc = r.Next(0, data.Length + CRC_LEN);
				if (err_loc >= data.Length)
					val_over = true;
				else
					data[err_loc] = (byte)(data[err_loc] ^ (byte)r.Next(1,255));
			}
		}

		protected delegate void SetTestingValsDelegate(int tests, int mist_1, int mist_2, double eff);
		protected void SetTestingVals(int tests, int mist_1, int mist_2, double eff)
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new SetTestingValsDelegate(this.SetTestingVals), tests, mist_1, mist_2, eff);
			}
			else
			{
				textBoxTestsCount.Text = tests.ToString();
				textBoxMistakes1type.Text = mist_1.ToString();
				textBoxMistakes2type.Text = mist_2.ToString();
				textBoxEfficiency.Text = string.Format("{0} %", eff);
			}
		}

		private Thread CRCTestingThread;
		private void buttonTest_Click(object sender, EventArgs e)
		{
			if (CRCTestingThread == null)
			{
				ThreadStart run_tests = this.RunTests;
				CRCTestingThread = new Thread(run_tests);
				CRCTestingThread.IsBackground = true;
				CRCTestingThread.Start();
				((Button) sender).Text = "Stop";
			}
			else
			{
				CRCTestingThread.Abort();
				CRCTestingThread= null;
				((Button)sender).Text = "Start";
			}
		}

		protected void RunTests()
		{
			int num_tests = 0;
			Int32.TryParse(textBoxNumOfTests.Text, out num_tests);
			if (num_tests == 0)
				num_tests = 1111111111;

			int curr_test;
			int mist_1 = 0;
			int mist_2 = 0;
			double eff;
			for (curr_test = 1; curr_test <= num_tests; ++curr_test)
			{
				byte[] data = this.GetData();
				byte[] crc_original = this.CalcCRC(data);
				this.MakeRandomErrors(data);
				byte[] crc_err = this.CalcCRC(data);

				bool diff = false;
				for (int j = 0; j < CRC_LEN; ++j)
				{
					if (crc_original[j] != crc_err[j])
						diff = true;
				}

				if (val_over)
					++mist_2;
				else if (!diff)
					++mist_1;


				eff = ((curr_test - mist_1 - mist_2) * 100.0) / ((double)(curr_test));

				this.SetTestingVals(curr_test, mist_1, mist_2, eff);
			}
		}
	}
}
